unit heContnrs_test;

{$mode objfpc}{$H+}

// Copyright 2010, bflm. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

interface

uses
  Classes,
  fpcunit,
  heContnrs;

type

  TTestEnumeratorIntInt = specialize TheEnumerator<Integer, Integer>;
  TTestEnumeratorIntStr = specialize TheEnumerator<Integer, String>;
  TTestEnumeratorObjInt = specialize TheEnumerator<TObject, Integer>;
  TTestEnumeratorStrInt = specialize TheEnumerator<String, Integer>;
  TTestEnumeratorStrStr = specialize TheEnumerator<String, String>;

  { TheEnumeratorTest }

  TheEnumeratorTest = class(TTestCase)
  strict private
    FIterator: Integer;
    FTestEnumeratorIntInt: TTestEnumeratorIntInt;
    FTestEnumeratorIntStr: TTestEnumeratorIntStr;
    FTestEnumeratorObjint: TTestEnumeratorObjInt;
    FTestEnumeratorStrInt: TTestEnumeratorStrInt;
    FTestEnumeratorStrStr: TTestEnumeratorStrStr;
    function GetCurrentIntInt(var Iterator: Integer): Integer;
    function GetCurrentIntStr(var Iterator: Integer): String;
    function GetCurrentObjInt(var Iterator: TObject): Integer;
    function GetCurrentStrInt(var Iterator: String): Integer;
    function GetCurrentStrStr(var Iterator: String): String;
    function MoveNextIntInt(var Iterator: Integer): Boolean;
    function MoveNextIntStr(var Iterator: Integer): Boolean;
    function MoveNextObjInt(var Iterator: TObject): Boolean;
    function MoveNextStrInt(var Iterator: String): Boolean;
    function MoveNextStrStr(var Iterator: String): Boolean;
  published
    procedure TestIntInt;
    procedure TestIntStr;
    procedure TestObjInt;
    procedure TestStrInt;
    procedure TestStrStr;
  end;

  { TTracedObject }

  TTracedObject = class
  private
    FID: Integer;
    FTracer: TBits;
  public
    constructor Create(const ATracer: TBits; const AID: Integer);
    destructor Destroy; override;
    property ID: Integer read FID;
    property Tracer: TBits read FTracer;
  end;

  { TInterfacedTracedObject }

  TInterfacedTracedObject = class(TInterfacedObject)
  private
    FID: Integer;
    FTracer: TBits;
  public
    constructor Create(const ATracer: TBits; const AID: Integer);
    destructor Destroy; override;
    property ID: Integer read FID;
    property Tracer: TBits read FTracer;
  end;

function TracedObjectCompareID(const A, B: TObject): Integer;

implementation

uses
  SysUtils,
  testregistry;

function TracedObjectCompareID(const A, B: TObject): Integer;
begin
  Result := (A as TTracedObject).ID - (B as TTracedObject).ID;
end;

const
  ENUMERATORTEST_K = 3;
  ENUMERATORTEST_N = 10;

{ TheEnumeratorTest }

procedure TheEnumeratorTest.TestIntInt;
var I: Integer;
begin
  FTestEnumeratorIntInt.Init(-1, @MoveNextIntInt, @GetCurrentIntInt);
  AssertEquals('Initial .Current', -ENUMERATORTEST_K, FTestEnumeratorIntInt.Current);
  for I := 0 to ENUMERATORTEST_N - 1 do begin
    AssertTrue(Format('.MoveNext, I = %d', [I]), FTestEnumeratorIntInt.MoveNext);
    AssertEquals('.Current', I * ENUMERATORTEST_K, FTestEnumeratorIntInt.Current);
  end;
  AssertFalse('Final .MoveNext', FTestEnumeratorIntInt.MoveNext);
end;

procedure TheEnumeratorTest.TestIntStr;
var I: Integer;
begin
  FTestEnumeratorIntStr.Init(-1, @MoveNextIntStr, @GetCurrentIntStr);
  AssertEquals('Initial .Current', IntToStr(-ENUMERATORTEST_K), FTestEnumeratorIntStr.Current);
  for I := 0 to ENUMERATORTEST_N - 1 do begin
    AssertTrue(Format('.MoveNext, I = %d', [I]), FTestEnumeratorIntStr.MoveNext);
    AssertEquals('.Current', IntToStr(I * ENUMERATORTEST_K), FTestEnumeratorIntStr.Current);
  end;
  AssertFalse('Final .MoveNext', FTestEnumeratorIntStr.MoveNext);
end;

procedure TheEnumeratorTest.TestStrInt;
var I: Integer;
begin
  FTestEnumeratorStrInt.Init('-1', @MoveNextStrInt, @GetCurrentStrInt);
  AssertEquals('Initial .Current', -ENUMERATORTEST_K, FTestEnumeratorStrInt.Current);
  for I := 0 to ENUMERATORTEST_N - 1 do begin
    AssertTrue(Format('.MoveNext, I = %d', [I]), FTestEnumeratorStrInt.MoveNext);
    AssertEquals('.Current', I * ENUMERATORTEST_K, FTestEnumeratorStrInt.Current);
  end;
  AssertFalse('Final .MoveNext', FTestEnumeratorStrInt.MoveNext);
end;

procedure TheEnumeratorTest.TestStrStr;
var I: Integer;
begin
  FTestEnumeratorStrStr.Init('-1', @MoveNextStrStr, @GetCurrentStrStr);
  AssertEquals('Initial .Current', IntToStr(-ENUMERATORTEST_K), FTestEnumeratorStrStr.Current);
  for I := 0 to ENUMERATORTEST_N - 1 do begin
    AssertTrue(Format('.MoveNext, I = %d', [I]), FTestEnumeratorStrStr.MoveNext);
    AssertEquals('.Current', IntToStr(I * ENUMERATORTEST_K), FTestEnumeratorStrStr.Current);
  end;
  AssertFalse('Final .MoveNext', FTestEnumeratorStrStr.MoveNext);
end;

procedure TheEnumeratorTest.TestObjInt;
var I: Integer;
begin
  FIterator := -1;
  FTestEnumeratorObjInt.Init(Self, @MoveNextObjInt, @GetCurrentObjInt);
  AssertEquals('Initial .Current', -ENUMERATORTEST_K, FTestEnumeratorObjInt.Current);
  for I := 0 to ENUMERATORTEST_N - 1 do begin
    AssertTrue(Format('.MoveNext, I = %d', [I]), FTestEnumeratorObjInt.MoveNext);
    AssertEquals('.Current', I * ENUMERATORTEST_K, FTestEnumeratorObjInt.Current);
  end;
  AssertFalse('Final .MoveNext', FTestEnumeratorObjInt.MoveNext);
end;

function TheEnumeratorTest.GetCurrentIntInt(var Iterator: Integer): Integer;
begin
  Result := ENUMERATORTEST_K * Iterator;
end;

function TheEnumeratorTest.GetCurrentIntStr(var Iterator: Integer): String;
begin
  Result := IntToStr(ENUMERATORTEST_K * Iterator);
end;

function TheEnumeratorTest.GetCurrentStrInt(var Iterator: String): Integer;
begin
  Result := ENUMERATORTEST_K * StrToInt(Iterator);
end;

function TheEnumeratorTest.GetCurrentStrStr(var Iterator: String): String;
begin
  Result := IntToStr(ENUMERATORTEST_K * StrToInt(Iterator));
end;

function TheEnumeratorTest.GetCurrentObjInt(var Iterator: TObject): Integer;
begin
  Result := ENUMERATORTEST_K * (Iterator as TheEnumeratorTest).FIterator;
end;

function TheEnumeratorTest.MoveNextIntInt(var Iterator: Integer): Boolean;
begin
  Inc(Iterator);
  Result := Iterator < ENUMERATORTEST_N;
end;

function TheEnumeratorTest.MoveNextIntStr(var Iterator: Integer): Boolean;
begin
  Inc(Iterator);
  Result := Iterator < ENUMERATORTEST_N;
end;

function TheEnumeratorTest.MoveNextStrInt(var Iterator: String): Boolean;
var I: Integer;
begin
  I := StrToInt(Iterator);
  Inc(I);
  Iterator := IntToStr(I);
  Result := I < ENUMERATORTEST_N;
end;

function TheEnumeratorTest.MoveNextStrStr(var Iterator: String): Boolean;
var I: Integer;
begin
  I := StrToInt(Iterator);
  Inc(I);
  Iterator := IntToStr(I);
  Result := I < ENUMERATORTEST_N;
end;

function TheEnumeratorTest.MoveNextObjInt(var Iterator: TObject): Boolean;
begin
  Inc((Iterator as TheEnumeratorTest).FIterator);
  Result := (Iterator as TheEnumeratorTest).FIterator < ENUMERATORTEST_N;
end;

{ TTracedObject }

constructor TTracedObject.Create(const ATracer: TBits; const AID: Integer);
begin
  inherited Create;
  FTracer := ATracer;
  FID := AID;
  if Tracer <> nil then
    Tracer[ID] := True;
end;

destructor TTracedObject.Destroy;
begin
  if Tracer <> nil then
    Tracer[ID] := False;
  inherited Destroy;
end;

{ TInterfacedTracedObject }

constructor TInterfacedTracedObject.Create(const ATracer: TBits; const AID: Integer);
begin
  inherited Create;
  FTracer := ATracer;
  FID := AID;
  if Tracer <> nil then
    Tracer[ID] := True;
end;

destructor TInterfacedTracedObject.Destroy;
begin
  if Tracer <> nil then
    Tracer[ID] := False;
  inherited Destroy;
end;

initialization
  RegisterTests([
    TheEnumeratorTest
  ]);

end.

